#include "epoll_poller.h"
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <sys/epoll.h>
#include <unistd.h>

constexpr int INIT_EVENT_LIST_SIZE = 16;

EpollPoller::EpollPoller() : epollfd_(::epoll_create1(EPOLL_CLOEXEC)), events_(INIT_EVENT_LIST_SIZE){
    if (epollfd_ < 0) {
        perror("epoll_create1 error");
        abort();
    }
}

EpollPoller::~EpollPoller(){
    ::close(epollfd_);
}

void EpollPoller::poll(int timeoutMs, std::vector<Channel*>& activeChannels){
    int numEvents = ::epoll_wait(epollfd_, events_.data(), static_cast<int>(events_.size()), timeoutMs);
    //std::cout << "epoll_wait returned " << numEvents << " events\n";
    if (numEvents < 0) {
        perror("epoll_wait error");
        return;
    }

    for (int i = 0; i < numEvents; i++) {
        int fd = events_[i].data.fd;
        auto it = channels_.find(fd);
        if (it != channels_.end()) {
            Channel* channel = it->second;
            channel->set_revents(events_[i].events);
            activeChannels.push_back(channel);
        }
    }

    if (numEvents == events_.size()) {
        events_.resize(events_.size() * 2);
    }
}

void EpollPoller::updateChannel(Channel* channel){
    int fd = channel->fd();
    struct epoll_event ev;
    memset(&ev, 0, sizeof(ev));
    ev.events = channel->events();
    ev.data.fd = fd;

    //std::cout << "updateChannel: fd=" << fd << ", events=" << ev.events << std::endl;

    if (channels_.find(fd) == channels_.end()) {
        if (::epoll_ctl(epollfd_, EPOLL_CTL_ADD, fd, &ev) < 0) {
            perror("epoll_ctl ADD error");
            abort();
        }else {
            channels_[fd] = channel;
        }
    }else {
        if (::epoll_ctl(epollfd_, EPOLL_CTL_MOD, fd, &ev) < 0) {
            perror("epoll_ctl MOD erroe");
            abort();
        }
    }
}

void EpollPoller::removeChannel(Channel* channel){
    int fd = channel->fd();
    struct epoll_event ev;
    memset(&ev, 0, sizeof(ev));
    ev.events = channel->events();
    ev.data.fd = fd;

    if (::epoll_ctl(epollfd_, EPOLL_CTL_DEL, fd, &ev) < 0) {
        perror("epoll_ctl DEL error");
    }
    channels_.erase(fd);
}